package com.newjiabo.applet.modules.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.ContentType;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSONObject;
import com.newjiabo.applet.modules.entity.dto.PlaceOrderDTO;
import com.newjiabo.common.ski.media.module.entity.dto.pay.Amount;
import com.newjiabo.common.ski.media.module.entity.dto.pay.Payer;
import com.newjiabo.common.ski.media.module.entity.dto.WeChatPayDto;
import com.newjiabo.common.core.domain.entity.SkiMember;
import com.newjiabo.common.core.enums.ResultCode;
import com.newjiabo.common.exception.ServiceException;
import com.newjiabo.common.ski.media.utils.WxPayUtil;
import com.newjiabo.common.utils.SecurityUtils;
import com.newjiabo.common.utils.SnowFlake;
import com.newjiabo.common.utils.StringUtils;
import com.newjiabo.common.ski.config.WeChatConfig;
import com.newjiabo.ski.domain.*;
import com.newjiabo.ski.domain.dto.SkiAfterSaleDTO;
import com.newjiabo.ski.domain.enums.AfterSaleEnum;
import com.newjiabo.ski.domain.enums.AfterSaleRefundWayEnum;
import com.newjiabo.ski.domain.enums.OrderStatusEnum;
import com.newjiabo.ski.domain.enums.SetMealStatus;
import com.newjiabo.ski.service.*;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;

import static com.github.wxpay.sdk.WXPayUtil.generateNonceStr;

/**
 * {类内容}
 *
 * @author CaoMengHui.
 * @version 1.0
 * @since 2023/9/22 11:30
 */
@Service
public class CashierService {
    @Resource
    private ISkiSnowFieldInfoService snowFieldInfoService;
    @Resource
    private ISkiSetMealService setMealService;
    @Resource
    private ISkiMoreServicesService moreServicesService;
    @Resource
    private ISkiOrderService orderService;
    @Resource
    private ISkiOrderDetailService orderDetailService;
    @Resource
    private ISkiOrderDetailServicesService orderDetailServicesService;
    @Resource
    private ConnectService connectService;
    @Resource
    private WxPayUtil wxPayUtil;
    @Resource
    private ISkiAfterSaleService afterSaleService;

    private static final Logger log = LoggerFactory.getLogger(CashierService.class);

    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> placeOrder(PlaceOrderDTO orderDTO) {
        String snowFieldId = orderDTO.getSnowFieldId();
        String setMealId = orderDTO.getSetMealId();
        List<String> servicesList = orderDTO.getServicesList();
        SkiSnowField skiSnowField = snowFieldInfoService.selectSkiSnowFieldById(snowFieldId);
        SkiSetMeal skiSetMeal = setMealService.selectSkiSetMealById(setMealId);
        List<SkiMoreServices> moreServicesList = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(servicesList)) {
            moreServicesList = moreServicesService.selectInIds(servicesList);
        }
        this.checkPlaceOrderStatus(skiSetMeal, moreServicesList);
        // 组装微信body
        String description = this.assessWxPayBody(skiSnowField.getSnowField(), skiSetMeal, moreServicesList);
        // 生成订单号
        String buildOrderNo = WxPayUtil.buildOrderNo();
        String orderId = SnowFlake.getIdStr();
        String memberId = SecurityUtils.getMemberId();
        SkiOrder skiOrder = new SkiOrder(orderId, buildOrderNo, snowFieldId, skiSnowField.getSnowField(), setMealId, skiSetMeal.getSetMealName(), memberId,
                OrderStatusEnum.canceled.name(), description, null, new Date(), orderDTO.getMediaIds(), orderDTO.getMediaCoverUrl(), orderDTO.getOutputDuration());
        // 先插入附表 详情表
        String orderDetailId = SnowFlake.getIdStr();
        SkiMember member = SecurityUtils.getLoginUser().getMember();
        SkiOrderDetail orderDetail = new SkiOrderDetail(orderDetailId, orderId, setMealId, skiSetMeal.getSetMealName(), skiSetMeal.getSetMealDesc(),
                skiSetMeal.getPrice(), skiSnowField.getId(), skiSnowField.getSnowField(), member.getMobile(), memberId, member.getNickName());
        orderDetailService.insertSkiOrderDetail(orderDetail);
        // 计算金额
        double total = skiSetMeal.getPrice();
        StringBuilder moreServicesIds = new StringBuilder();
        if (CollectionUtil.isNotEmpty(moreServicesList)) {
            List<SkiOrderDetailServices> orderDetailServicesList = new ArrayList<>();
            for (SkiMoreServices skiMoreServices : moreServicesList) {
                total += skiMoreServices.getPrice();
                moreServicesIds.append(skiMoreServices.getServicesId());
                moreServicesIds.append(",");
                // 附表
                SkiOrderDetailServices orderDetailServices = new SkiOrderDetailServices(SnowFlake.getIdStr(), orderDetailId, skiMoreServices.getServicesId(), skiMoreServices.getServicesName(), skiMoreServices.getServicesDesc(), skiMoreServices.getPrice());
                orderDetailServicesList.add(orderDetailServices);
            }
            moreServicesIds.substring(0, moreServicesIds.length() - 1);
            skiOrder.setMoreServicesId(moreServicesIds.toString());
            // 插入附表中的服务类别表
            orderDetailServicesService.insertBatch(orderDetailServicesList);
        }
        skiOrder.setTotal(total);
        orderService.insertSkiOrder(skiOrder);
        WeChatPayDto weChatPayDto = new WeChatPayDto(description, buildOrderNo, WeChatConfig.getNotifyUrl(),
                new Amount(WxPayUtil.YUANConvertToFen(total), "CNY"), new Payer(connectService.selectByMemberId(memberId).getUnionId()));
        // TODO <CaoMengHui>: 组装前段返回的参数（这里需要封装）
        Map<String, Object> resultMap = new HashMap<>(4);
        resultMap.put("selectedMaterials", orderDTO.getMediaIds().split(",").length);
        resultMap.put("createTime", DateUtil.format(skiOrder.getCreateTime(), "yyyy-MM-dd HH:mm:ss"));
        resultMap.put("total", total);
        resultMap.put("weChatPayDto", weChatPayDto);
        return resultMap;
    }


    private String assessWxPayBody(String snowFieldName, SkiSetMeal skiSetMeal, List<SkiMoreServices> moreServicesList) {
        String sb = "你在" + snowFieldName + "下购买的套餐为：" + skiSetMeal.getSetMealName() +
                "。套餐金额为：" + skiSetMeal.getPrice();
        return sb;
    }

    /**
     * 检查商品状态
     *
     * @param skiSetMeal          套餐
     * @param skiMoreServicesList 服务
     * @return
     * @author CaoMengHui.
     * @date 2023/9/22 13:55
     */
    private void checkPlaceOrderStatus(SkiSetMeal skiSetMeal, List<SkiMoreServices> skiMoreServicesList) {
        if (ObjectUtil.isNotEmpty(skiSetMeal)) {
            if (SetMealStatus.CLOSE.name().equals(skiSetMeal.getSetMealStatus()) || skiSetMeal.getDelFlag()) {
                throw new ServiceException(ResultCode.PACKAGE_INFORMATION_ERROR);
            }
        }
        if (CollectionUtil.isNotEmpty(skiMoreServicesList)) {
            for (SkiMoreServices skiMoreServices : skiMoreServicesList) {
                if (!skiMoreServices.getStatus() || skiMoreServices.getDelFlag()) {
                    throw new ServiceException(ResultCode.MORE_SERVICE_INFORMATION_ERROR);
                }
            }
        }
    }

    public Map<String, Object> pullUpPayment(WeChatPayDto weChatPayDto, HttpServletRequest request) {
        try {
            CloseableHttpResponse prepayInfo = wxPayUtil.getPrepayInfo(weChatPayDto);
            //如果状态码为200者正常
            int statusCode = prepayInfo.getStatusLine().getStatusCode();
            log.info("下单状态为：{}", statusCode);
            String orderStr = EntityUtils.toString(prepayInfo.getEntity(), StandardCharsets.UTF_8);
            log.info("下单结果为：{}", orderStr);
            Map<String, Object> resultMap = new HashMap<>(6);
            resultMap.put("statusCode", statusCode);
            if (statusCode == 200) {
                JSONObject orderStrObj = JSONObject.parseObject(orderStr);
                // 拿到最重要的prepay_id
                String prepayId = orderStrObj.getString("prepay_id");
                String packageStr = StringUtils.join("prepay_id=", prepayId);
                // 生成代签名的支付信息
                String nonceStr = generateNonceStr();
                String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
                String signMessage = wxPayUtil.buildSignMessage(packageStr, nonceStr, timestamp);
                log.info("签名串为：{}", signMessage);
                String signature = wxPayUtil.sign(signMessage.getBytes());
                resultMap.put("appid", WeChatConfig.getAppid());
                resultMap.put("package", packageStr);
                resultMap.put("nonceStr", nonceStr);
                resultMap.put("timeStamp", timestamp);
                resultMap.put("paySign", signature);
                return resultMap;
            } else {
                resultMap.put("orderStr", orderStr);
                return resultMap;
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public void payCallback(HttpServletRequest request, HttpServletResponse response) {
        BufferedReader br = null;
        try {
            JSONObject dataJSON = wxPayUtil.getCallback(request);
            // 解密前的结果
            if (ObjectUtils.isEmpty(dataJSON)) {
                log.error("解密失败，拿不到数据数据为空！");
                return;
            }
            log.info("======解密成功！=========");
            // 商户平台订单号
            String outTradeNo = dataJSON.getString("out_trade_no");
            //微信支付交易号
            String transactionId = dataJSON.getString("transaction_id");
            // 支付完成时间
            Date successTime = new SimpleDateFormat("yyyy-MM-DDTHH:mm:ss+TIMEZONE").parse(dataJSON.getString("success_time"));
            // 支付实际金额
            JSONObject amount = dataJSON.getJSONObject("amount");
            double payTotal = amount.getDoubleValue("payer_total") / 100;
            //处理业务逻辑 更新状态
            Boolean handleFag = orderService.orderStatusHandle(outTradeNo, transactionId, OrderStatusEnum.paid,successTime,payTotal);
            Map<String, String> map = new HashMap<String, String>(2);
            if (handleFag) {
                response.setStatus(200);
                map.put("code", "SUCCESS");
                map.put("message", "SUCCESS");
            } else {
                log.error("系统出错！");
                return;
            }
            //返回给微信
            response.setHeader("Content-type", ContentType.JSON.toString());
            response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
            response.flushBuffer();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 申请售后
     * 1、改变主订单状态
     *
     * @param dto
     * @return
     * @author CaoMengHui.
     * @date 2023/9/23 16:41
     */
    @Transactional(rollbackFor = Exception.class)
    public SkiAfterSale applyService(SkiAfterSaleDTO dto) {
        this.checkAfterSale(dto);
        SkiAfterSale skiAfterSale = BeanUtil.copyProperties(dto, SkiAfterSale.class);
        this.initAfterSale(skiAfterSale);
        afterSaleService.insertSkiAfterSale(skiAfterSale);
        orderService.updateStatusByOrderId(dto.getOrderId(), OrderStatusEnum.applyRefund);
        return skiAfterSale;
    }

    private void initAfterSale(SkiAfterSale skiAfterSale) {
        String afterSaleId = SnowFlake.getIdStr();
        skiAfterSale.setId(afterSaleId);
        SkiMember member = SecurityUtils.getLoginUser().getMember();
        skiAfterSale.setMemberId(member.getId());
        skiAfterSale.setMemberName(member.getNickName());
        skiAfterSale.setServiceStatus(AfterSaleEnum.applyRefund.name());
        skiAfterSale.setRefundWay(AfterSaleRefundWayEnum.ORIGINAL.name());
        skiAfterSale.setOutRefundNo(WxPayUtil.buildRefundNo());
    }

    /**
     * 检查订单状态
     *
     * @param dto
     * @return
     * @author CaoMengHui.
     * @date 2023/9/23 16:42
     */
    private void checkAfterSale(SkiAfterSaleDTO dto) {
        // 检查订单信息是否为空！
        if (ObjectUtil.isNull(dto) || StringUtils.isEmpty(dto.getOrderId()) || StringUtils.isEmpty(dto.getOutTradeNo())) {
            throw new ServiceException(ResultCode.ORDER_NOT_EXIST);
        }
        SkiOrder skiOrder = orderService.selectSkiOrderByOrderId(dto.getOrderId());
        // 检查订单信息，退款金额不能大于支付金额，支付完成的订单才能申请退款
        if (!OrderStatusEnum.paid.name().equals(skiOrder.getOrderStatus())) {
            throw new ServiceException(ResultCode.REFUND_STATUS_ERROR);
        }
        if (dto.getApplyRefundPrice() > skiOrder.getPayTotal()) {
            throw new ServiceException(ResultCode.REFUND_STATUS_ERROR);
        }
        // 初始化售后订单信息
        if(ObjectUtil.isNull(dto.getFlowPrice())){
            dto.setFlowPrice(skiOrder.getPayTotal());
        }
    }


    public void refundWeChatNotify(HttpServletRequest request, HttpServletResponse response) {
        try {
            JSONObject callback = wxPayUtil.getCallback(request);
            if(ObjectUtil.isNull(callback)){
                log.error("微信退款回调信息解谜失败！");
                return;
            }
            log.info("=================解谜成功=================");
            // 退款时间 service_status
            String refundStatus = callback.getString("refund_status");
            String outTradeNo = callback.getString("out_trade_no");
            String transactionId = callback.getString("transaction_id");
            String outRefundNo = callback.getString("out_refund_no");
            SkiAfterSale afterSale = afterSaleService.selectByOrder(null, outRefundNo, outTradeNo);
            if(ObjectUtil.isNull(afterSale)){
                log.error("商户订单号：{}，在数据库中不存在。",outTradeNo);
                return;
            }
            afterSale.setWxRefundStatus(refundStatus);
            String refundId = callback.getString("refund_id");
            if("SUCCESS".equals(refundStatus)){
                afterSale.setRefundId(refundId);
                // 退款时间
                Date successTime = new SimpleDateFormat("yyyy-MM-DDTHH:mm:ss+TIMEZONE").parse(callback.getString("success_time"));
                afterSale.setRefundTime(successTime);
            }else {
                log.error("微信退款回调失败，商户订单号为：{}",outTradeNo);
            }
            afterSaleService.updateSkiAfterSale(afterSale);
            //返回给微信
            Map<String,String> wxResultMap = new HashMap<>(2);
            response.setStatus(200);
            wxResultMap.put("code", "SUCCESS");
            wxResultMap.put("message", "SUCCESS");
            response.setHeader("Content-type", ContentType.JSON.toString());
            response.getOutputStream().write(JSONUtil.toJsonStr(wxResultMap).getBytes(StandardCharsets.UTF_8));
            response.flushBuffer();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }

    }
}
